﻿using System;
using System.Collections.Generic;

namespace SlamCms.Collections
{
    public sealed class ComparableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, IEquatable<ComparableDictionary<TKey, TValue>>
    {
        private readonly Dictionary<TKey, TValue> _dictionary;

        public ComparableDictionary()
        {
            _dictionary = new Dictionary<TKey, TValue>();
        }

        public ComparableDictionary(IDictionary<TKey, TValue> dictionary)
        {
            _dictionary = new Dictionary<TKey, TValue>(dictionary);
        }

        public ComparableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer)
        {
            _dictionary = new Dictionary<TKey, TValue>(dictionary, comparer);
        }

        public ComparableDictionary(IEqualityComparer<TKey> comparer)
        {
            _dictionary = new Dictionary<TKey, TValue>(comparer);
        }

        public override bool Equals(object other)
        {
            return Equals(other as ComparableDictionary<TKey, TValue>);
        }

        public bool Equals(ComparableDictionary<TKey, TValue> other)
        {
            if (ReferenceEquals(other, null))
                return false;
            
            if (Count != other.Count)
                return false;

            foreach (var pair in this)
            {
                TValue otherValue;
                if (!other.TryGetValue(pair.Key, out otherValue))
                    return false;
                
                if (!EqualityComparer<TValue>.Default.Equals(pair.Value, otherValue))
                    return false;
            }

            return true;
        }

        public override int GetHashCode()
        {
            int hash = 0;

            foreach (var pair in this)
            {
                int miniHash = 17;
                miniHash = miniHash * 31 + EqualityComparer<TKey>.Default.GetHashCode(pair.Key);
                miniHash = miniHash * 31 + EqualityComparer<TValue>.Default.GetHashCode(pair.Value);
                hash ^= miniHash;
            }

            return hash;
        }

        public void Add(TKey key, TValue value)
        {
            _dictionary.Add(key, value);
        }

        public bool ContainsKey(TKey key)
        {
            return _dictionary.ContainsKey(key);
        }

        public ICollection<TKey> Keys
        {
            get { return _dictionary.Keys; }
        }

        public bool Remove(TKey key)
        {
            return _dictionary.Remove(key);
        }

        public bool TryGetValue(TKey key, out TValue value)
        {
            return _dictionary.TryGetValue(key, out value);
        }

        public ICollection<TValue> Values
        {
            get { return _dictionary.Values; }
        }

        public TValue this[TKey key]
        {
            get { return _dictionary[key]; }
            set { _dictionary[key] = value; }
        }

        public void Add(KeyValuePair<TKey, TValue> item)
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Add(item);
        }

        public void Clear()
        {
            _dictionary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item)
        {
            throw new NotImplementedException();
        }

        void ICollection<KeyValuePair<TKey, TValue>>.CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return _dictionary.Count; }
        }

        bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly
        {
            get { return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).IsReadOnly; }
        }

        public bool Remove(KeyValuePair<TKey, TValue> item)
        {
            return ((ICollection<KeyValuePair<TKey, TValue>>)_dictionary).Remove(item);
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return _dictionary.GetEnumerator();
        }
    }
}
